#include <stdio.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "json_load.h"
#include "./sproto.h"
#include "./sprotoc_common.h"

#define UNUSED(x) (void)(x)

int traverse_array(json_object *L, struct field *f, void *data, uint32_t *n_size)
{
	std::vector<uint64_t> vec_int;
	std::vector<char *> vec_str;

	assert(json_object_get_type(L) == json_type_array);
	assert(f->type & SPROTO_TARRAY);
	int f_type = f->type & ~SPROTO_TARRAY;

	int n = json_object_array_length(L);
	for (int i = 0; i < n; ++i)
	{
		json_object *val = json_object_array_get_idx(L, i);
//		const char *c = json_object_to_json_string(val);
		const char *c = json_object_get_string(val);
		if (f_type == SPROTO_TINTEGER)
		{
			uint64_t n = strtoul(c, NULL, 0);
			vec_int.push_back(n);
		}
		else
		{
			char *n = strdup(c);
			vec_str.push_back(n);
		}
	}

	if (f_type == SPROTO_TSTRING)
	{
		*n_size = vec_str.size();
		char ***p = (char ***)((char *)data + f->offset + sizeof(uint32_t));
		*p = (char **)malloc(sizeof(char *) * (*n_size));
		for (ssize_t i = 0; i < (*n_size); ++i)
			(*p)[i] = vec_str[i];
	}
	else if (f_type == SPROTO_TINTEGER)
	{
		*n_size = vec_int.size();
		uint64_t **p = (uint64_t **)((char *)data + f->offset + sizeof(uint32_t));
		(*p) = (uint64_t *)malloc(sizeof(uint64_t) * (*n_size));
		for (ssize_t i = 0; i < (*n_size); ++i)
			(*p)[i] = vec_int[i];		
	}
	
	return (0);
}

int traverse_table(json_object *L, struct sproto_type *sproto_type, void *data)
{
	assert(json_object_get_type(L) == json_type_object);
	json_object_object_foreach(L, key, val)
	{
		struct field *f = NULL;
		int i;
		for (i = 0; sproto_type && i < sproto_type->n; ++i)
		{
			const char *type_name = sproto_type->f[i].name;
			// if (key[0] >= '0' && key[0] <= '9')
			// {
			// 	type_name++;
			// }
			if (strcmp(type_name, key) == 0)
			{
				f = &sproto_type->f[i];
				break;
			}
		}

		if (!f)
			continue;

		if (f->type & SPROTO_TARRAY)
		{
			uint32_t *n_size = (uint32_t *)((char *)data + f->offset);
			traverse_array(val, f, data, n_size);
		}
		else
		{
//			const char *c = json_object_to_json_string(val);
			const char *c = json_object_get_string(val);
			if (f->type == SPROTO_TINTEGER)
				*(uint64_t *)((char *)data + f->offset) = strtoul(c, NULL, 0);
			else if (f->type == SPROTO_TDOUBLE)
				*(double *)((char *)data + f->offset) = strtod(c, NULL);
			else
				*(char **)((char *)data + f->offset) = strdup(c);
		}
	}
	return 0;
}

int traverse_main_table(struct sproto_type *sproto_type, const char *json_file_name, std::map<uint64_t, void *> *ret_m, std::vector<void *> *ret_v)
{
	int fd = open(json_file_name, O_RDONLY);
	if (fd < 0)
	{
		return -1;
	}
	struct stat statbuf;
	fstat(fd, &statbuf);
	char *outStr = (char *)malloc(statbuf.st_size + 1);
	if (!outStr)
	{
		close(fd);
		return -10;
	}
	if (read(fd, outStr, statbuf.st_size) != statbuf.st_size)
	{
		free(outStr);
		close(fd);
		return -20;
	}
	close(fd);
	outStr[statbuf.st_size] = '\0';
	
	json_object *new_obj = json_tokener_parse(outStr);
	json_object_object_foreach(new_obj, key, val)
	{
		UNUSED(key);
		json_object_object_foreach(val, key1, val1)
		{
			uint64_t id = strtoul(key1, NULL, 0);
			void *data = malloc(sproto_type->c_size);
			memset(data, 0, sproto_type->c_size);
			traverse_table(val1, sproto_type, data);
			if (ret_m)
				(*ret_m)[(uint64_t)id] = data;
			if (ret_v)
				ret_v->push_back(data);
		}
	}

	json_object_put(new_obj);	
	free(outStr);
	return (0);
}

int traverse_vec_table(struct sproto_type *sproto_type, const char *json_file_name, std::vector<void *> *ret)
{
	int fd = open(json_file_name, O_RDONLY);
	if (fd < 0)
	{
		return -1;
	}
	struct stat statbuf;
	fstat(fd, &statbuf);
	char *outStr = (char *)malloc(statbuf.st_size + 1);
	if (!outStr)
	{
		close(fd);
		return -10;
	}
	if (read(fd, outStr, statbuf.st_size) != statbuf.st_size)
	{
		free(outStr);
		close(fd);
		return -20;
	}
	close(fd);
	outStr[statbuf.st_size] = '\0';
	
	json_object *new_obj = json_tokener_parse(outStr);
	json_object_object_foreach(new_obj, key, val)
	{
		UNUSED(key);
		json_object_object_foreach(val, key1, val1)
		{
			UNUSED(key1);
			// uint64_t id = strtoul(key1, NULL, 0);
			void *data = malloc(sproto_type->c_size);
			memset(data, 0, sproto_type->c_size);
			traverse_table(val1, sproto_type, data);
			ret->push_back(data);
		}
	}

	json_object_put(new_obj);	
	free(outStr);
	return (0);
}
